﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace ConfigPatch
{
    public class XmlMerger
    {
        private static string[] IDENTITY_ATTRIBUTES = new string[] { "name" , "key" };
        TextWriter log;

        public XmlMerger(TextWriter log)
        {
            this.log = log;
        }

        public void MergeNode(XmlElement xmlElement, XmlDocument doc)
        {
            string fullpath = GetNodePath(xmlElement);
            if (isMergable(xmlElement))
            {
                string idAttribute = GetIdentityAttribute(xmlElement);
                string idValue = xmlElement.Attributes[idAttribute].Value;
                fullpath = fullpath + string.Format("[@{0}='{1}']", idAttribute, idValue);

                XmlElement destNode = doc.SelectSingleNode(fullpath) as XmlElement;

                log.Write("Merging {0}", fullpath);
                DoMerge(xmlElement, destNode);
                log.WriteLine();
            }
            else
            {
                log.WriteLine("Merging {0} ", fullpath);
                foreach (XmlElement child in xmlElement.ChildNodes)
                {
                    MergeNode(child, doc);
                }
            }
        }

        private void DoMerge(XmlElement xmlElement, XmlElement destNode)
        {
            if (destNode == null)
            {
                log.Write("missing");
            }
            else
            {
                // copy attributes
                foreach (XmlAttribute attr in xmlElement.Attributes)
                {
                    log.Write(".");
                    destNode.SetAttribute(attr.Name, attr.Value);
                }
            }
        }

        private static string GetNodePath(XmlElement xmlElement)
        {
            XmlElement current = xmlElement.ParentNode as XmlElement;
            string path = xmlElement.Name;
            while (current != null)
            {
                path = current.Name + "/" + path;
                current = current.ParentNode as XmlElement;
            }
            return path;
        }

        private static bool isMergable(XmlElement xmlElement)
        {
            return (GetIdentityAttribute(xmlElement) != null);
        }

        private static string GetIdentityAttribute(XmlElement node)
        {
            foreach (string attr in IDENTITY_ATTRIBUTES)
            {
                if (node.HasAttribute(attr))
                    return attr;
            }
            return null;
        }
    }
}
